
#pragma once
#include <CGAL/Cartesian.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel_with_sqrt.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Nef_polyhedron_3.h>
#include <string>

///the properties of a material excluding the ambient specular and diffuse
class material_prop
{
private:
	float m_Ns, m_Ni, m_d, m_Tr, m_Tf[3], m_Ks[3];
	int m_illum;
public:
	material_prop(){}
	material_prop(float _Ns, float _Ni, float _d, float _Tr, float _Tf1, float _Tf2, float _Tf3, float _Ks1,
		float _Ks2, float _Ks3, int _illum)
	{
		m_Ns = _Ns;
		m_Ni = _Ni;
		m_d = _d;
		m_Tr = _Tr;
		m_Tf[0]= _Tf1;
		m_Tf[1]= _Tf2;
		m_Tf[2]= _Tf3;
		m_Ks[0]= _Ks1;
		m_Ks[1]= _Ks2;
		m_Ks[2]= _Ks3;
		m_illum = _illum;
	}
public:
	void set_prop(float _Ns, float _Ni, float _d, float _Tr, float _Tf1, float _Tf2, float _Tf3, float _Ks1,
		float _Ks2, float _Ks3, int _illum)
	{
		m_Ns = _Ns;
		m_Ni = _Ni;
		m_d = _d;
		m_Tr = _Tr;
		m_Tf[0]= _Tf1;
		m_Tf[1]= _Tf2;
		m_Tf[2]= _Tf3;
		m_Ks[0]= _Ks1;
		m_Ks[1]= _Ks2;
		m_Ks[2]= _Ks3;
		m_illum =_illum;
	}
	float get_Ns(){return m_Ns;}
	float get_Ni(){return m_Ni;}
	float get_d(){return m_d;}
	float get_Tr(){return m_Tr;}
	float* get_Tf(){return m_Tf;}
	float* get_Ks(){return m_Ks;}
	int get_illum(){return m_illum;}
};

//a material object
class simple_material_obj
{
private:
	std::string name;
	float Ka[3], Kd[3], Ks[3];//ambient diffuse specular
public:
	simple_material_obj(){}
	simple_material_obj(std::string n, float ka0, float ka1, float ka2, float kd0, float kd1, float kd2,
		float ks0, float ks1, float ks2)
	{
		name = n;
		Ka[0] = ka0;Ka[1] = ka1;Ka[2] = ka2;
		Kd[0] = kd0;Kd[1] = kd1;Kd[2] = kd2;
		Ks[0] = ks0;Ks[1] = ks1;Ks[2] = ks2;
	}
	simple_material_obj(std::string n, float* ka, float* kd, float* ks)
	{
		name = n;
		Ka[0] = ka[0];Ka[1] = ka[1];Ka[2] = ka[2];
		Kd[0] = kd[0];Kd[1] = kd[1];Kd[2] = kd[2];
		Ks[0] = ks[0];Ks[1] = ks[1];Ks[2] = ks[2];
	}
public:
	std::string get_name()
	{return name;}
	void set_name(std::string n)
	{name = n;}
	float* get_ambient()
	{return Ka;}
	void set_ambient(float* ka)
	{Ka[0] = ka[0];Ka[1] = ka[1];Ka[2] = ka[2];}
	void set_ambient(float ka0, float ka1, float ka2)
	{Ka[0] = ka0;Ka[1] = ka1;Ka[2] = ka2;}
	float* get_diffuse()
	{return Kd;}
	void set_diffuse(float* kd)
	{Kd[0] = kd[0];Kd[1] = kd[1];Kd[2] = kd[2];}
	void set_diffuse(float kd0, float kd1, float kd2)
	{Kd[0] = kd0;Kd[1] = kd1;Kd[2] = kd2;}
	float* get_specular()
	{return Ks;}
	void set_specular(float* ks)
	{Ks[0] = ks[0];Ks[1] = ks[1];Ks[2] = ks[2];}
	void set_specular(float ks0, float ks1, float ks2)
	{Ks[0] = ks0;Ks[1] = ks1;Ks[2] = ks2;}
};

//for calculating the normal of faces
struct Plane_equation
{
	template <class Facet>
	typename Facet::Plane_3 operator()(Facet& f)
	{
		typename Facet::Halfedge_handle h = f.halfedge();
		typedef typename Facet::Plane_3 Plane;
		return Plane(h->vertex()->point(),h->next()->vertex()->point(),
			h->next()->next()->vertex()->point());
	}
};

template <class Refs, class Plane>
struct My_face : public CGAL::HalfedgeDS_face_base<Refs, CGAL::Tag_true, Plane>{
	//extended property for a face
	simple_material_obj mtl;
	My_face(){}
	My_face(simple_material_obj m) : mtl(m){}
};

struct My_items : public CGAL::Polyhedron_items_3{
	template<class Refs,class Traits>
	struct Face_wrapper{
		typedef typename Traits::Plane_3 Plane;
		typedef My_face<Refs, Plane> Face;
	};
}; 

//kernels (choose the least best)
typedef CGAL::Exact_predicates_inexact_constructions_kernel IK;
typedef CGAL::Exact_predicates_exact_constructions_kernel EK;

//predefined
typedef CGAL::Polyhedron_3<IK,My_items> Polyhedron_3;//IK
typedef CGAL::Nef_polyhedron_3<EK> Nef_polyhedron_3;//EK

//for convert
typedef CGAL::Polyhedron_3<EK,My_items> EK_Poly_3;

//thresholds
#define NORMAL_DIF_TOL 1e-4 //for co-planer merging
#define DEGENERATION_TEST 1e-2 // for degeneration test
